<?php declare(strict_types=1);
//ssl加密key&iv
define('SSL_KEY', 'liChengLong');
define('SSL_IV', 'aBoXxLgt47t546sd');

define('BASE_PATH', dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR);

defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL);

/**
 * 生成数学计算公式图形验证码
 * @param int $w
 * @param int $h
 * @param string $strUniqueKey
 */
function getOperationCaptcha(int $w, int $h, string $strUniqueKey): void
{
        $img = imagecreate($w, $h);
        $gray = imagecolorallocate($img, 255, 255, 255);
        $black = imagecolorallocate($img, rand(0, 200), rand(0, 200), rand(0, 200));
        $red = imagecolorallocate($img, 255, 0, 0);
        $white = imagecolorallocate($img, 255, 255, 255);
        $green = imagecolorallocate($img, 0, 255, 0);
        $blue = imagecolorallocate($img, 0, 0, 255);
        imagefilledrectangle($img, 0, 0, 100, 30, $black);
        for ($i = 0; $i < 80; $i++) {
                imagesetpixel($img, rand(0, $w), rand(0, $h), $gray);
        }
        $num1 = mt_rand(1, 20);
        $num2 = mt_rand(1, 20);
        $operator = getRandOperationType();
        $strCaptcha = '';
        if ($operator == '+') {
                $strCaptcha = (string)($num1 + $num2);
        } else if ($operator == '-') {
                $strCaptcha = (string)($num1 - $num2);
        }
        \Swoft\Redis\Redis::set($strUniqueKey . 'OperationCaptcha', $strCaptcha, 180);
        imagestring($img, 5, 5, rand(1, 10), (string)$num1, $red);
        imagestring($img, 5, 30, rand(1, 10), $operator, $white);
        imagestring($img, 5, 45, rand(1, 10), (string)$num2, $green);
        imagestring($img, 5, 65, rand(1, 10), "=", $blue);
        imagestring($img, 5, 80, rand(1, 10), "?", $red);
        imagepng($img);
        imagedestroy($img);
}

/**
 * 随机返回运算方式
 * @return string
 */
function getRandOperationType(): string
{
        $intCode = mt_rand(0, 1);
        switch ($intCode) {
                case 0:
                        return "+";
                        break;
                case 1:
                        return "-";
                        break;
                default:
                        # code...
                        break;
        }
}

/**
 * session 便用函数
 *
 * @return \Swoft\Http\Session\HttpSession::current
 */
function session(): object
{
        $sessionCurrent = \Swoft\Http\Session\HttpSession::current();
        return $sessionCurrent;
}

/**
 * 解密字符串
 * @param $data
 * @param string $key
 * @param string $iv
 * @return false|string
 */
function decryptOpenssl($data, string $key, string $iv)
{
        return openssl_decrypt(base64_decode($data), "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv);
}

/**
 * 加密字符串
 * @param string $data 字符串
 * @param string $key 加密key
 * @return string
 */
function encryptOpenssl($data, string $key, string $iv): string
{
        return base64_encode(openssl_encrypt($data, "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv));
}

/**
 * 返回请求对象
 * @return mixed
 */
function request()
{
        return \context()->getRequest();
}

/**
 * 返回响应对象
 * @return mixed
 */
function response()
{
        return \context()->getResponse();
}

/**
 * json响应
 * @param array $arrData
 * @return mixed
 */
function jsonResponse(array $arrData)
{
        return response()->withContentType(\Swoft\Http\Message\ContentType::JSON)->withData($arrData);
}

/**
 * 获取get参数
 * @param string $strKey
 * @param string $strDefault
 * @return mixed
 */
function get(string $strKey = '', string $strDefault = '')
{
        if ($strKey) {
                return request()->get($strKey, $strDefault);
        }
        return request()->get();
}

/**
 * 获取post参数
 * @param string $strKey
 * @param string $strDefault
 * @return mixed
 */
function post(string $strKey = '', string $strDefault = '')
{
        if ($strKey) {
                return request()->post($strKey, $strDefault);
        }
        return request()->post();
}

/**
 * 获取input参数
 * @param string $strKey
 * @param string $strDefault
 * @return mixed
 */
function inputParameter(string $strKey = '', string $strDefault = '')
{
        if ($strKey) {
                return request()->input($strKey, $strDefault);
        }
        return request()->input();
}

/**
 * 获取raw参数
 * @return mixed
 */
function raw()
{
        return request()->raw();
}


/**
 * 获取路由匹配结果
 * @param int $index
 * @return mixed
 */
function route(int $index = null)
{
        $arrRoute = request()->getAttribute(\Swoft\Http\Message\Request::ROUTER_ATTRIBUTE);
        if ($index === null) return $arrRoute;
        return $arrRoute[$index];
}

/**
 * 获取请求query
 * @return mixed
 */
function query()
{
        return request()->getUri()->getQuery();
}

/**
 * 获取ip
 * @return mixed
 */
function ip()
{
        return ($arr = request()->getHeader('x-real-ip')) ? $arr[0] : '';
}

/**
 * 获取上传文件
 * @return mixed
 */
function getUploadedFiles()
{
        return request()->getUploadedFiles();
}

/**
 * 获取用户代理
 * @return mixed
 */
function userAgent()
{
        return request()->getHeader('user-agent')[0];
}

/**
 * 获取网站域名
 * @return string
 */
function getDomain()
{
        $strPrefix = explode('://', request()->getHeader('referer')[0])[0];
        return $strPrefix . '://' . request()->getHeader('host')[0];
}

/**
 * 回调参数组装函数
 * @param int $intCode
 * @param bool $boolSuccess
 * @param $data
 * @param string|null $strMsg
 * @param int|null $intCount
 * @return array
 */
function callbackParam(int $intCode, bool $boolSuccess, $data, string $strMsg = null)
{
        $arrReturn = ['code' => $intCode, 'success' => $boolSuccess, 'data' => $data];
        if ($strMsg !== null) $arrReturn['message'] = $strMsg;
        return $arrReturn;
}

/**
 * 获取欢迎语
 * @return string
 */
function getHello()
{
        //获取当前时间的 小时 单位
        $h = date('H');
        if ($h < 6) {
                $time = '凌晨好';
        } else if ($h < 9) {
                $time = '早上好';
        } else if ($h < 12) {
                $time = '上午好';
        } else if ($h < 14) {
                $time = '中午好';
        } else if ($h < 17) {
                $time = '下午好';
        } else if ($h < 19) {
                $time = '傍晚好';
        } else if ($h < 22) {
                $time = '晚上好';
        } else {
                $time = '深夜好';
        }
        return $time;
}

/**
 * 将数组指定的下标值作为键名
 * @param array $arrData
 * @param string $strKey
 * @param array $arrFormatJsonKey
 * @return array
 */
function arrayValueToKey(array $arrData, string $strKey, array $arrFormatJsonKey = []): array
{
        $arrReturn = [];
        foreach ($arrFormatJsonKey as $formatName) {
                foreach ($arrData as $k => $v) {
                        if (isset($v[$formatName])) {
                                $arrData[$k][$formatName] = json_decode($v[$formatName], true);
                        }
                }
        }
        foreach ($arrData as $k => $v) {
                $arrReturn[$v[$strKey]] = $v;
        }
        return $arrReturn;
}


/**
 * 递归数组
 * @param array $arr
 * @param int $pid
 * @param string $str_child_name
 * @param string $str_field_1
 * @param string $str_field_2
 * @return array
 */
function recursionArr(array $arr, int $pid = 0, string $str_child_name = 'child', string $str_field_1 = 'pid', string $str_field_2 = 'id'): array
{
        $array = [];
        foreach ($arr as $value) {
                if ($value[$str_field_1] == $pid) {
                        $value[$str_child_name] = recursionArr($arr, $value[$str_field_2], $str_child_name, $str_field_1, $str_field_2);
                        $array[] = $value;
                }
        }
        return $array;
}

/**
 * 判断数组下标是否设置未设置返回默认值
 * @param array $arrData
 * @param $strKey
 * @param null $defaultValue
 * @param bool $booleValIsExist
 * @return mixed|null
 */
function issetArrKey(array $arrData, $strKey, $defaultValue = null, bool $booleValIsExist = true)
{
        if (isset($arrData[$strKey])) {
                if ($booleValIsExist && $arrData[$strKey]) {
                        return $arrData[$strKey];
                }
        }
        return $defaultValue;
}

/**
 * 生成唯一id
 * @param string $prefix
 * @return string
 */
function uuid(string $prefix = 'lcl'): string
{
        $chars = md5(uniqid((string)mt_rand(), true));
        $uuid = substr($chars, 0, 8) . '-';
        $uuid .= substr($chars, 8, 4) . '-';
        $uuid .= substr($chars, 12, 4) . '-';
        $uuid .= substr($chars, 16, 4) . '-';
        $uuid .= substr($chars, 20, 12);
        return $prefix . $uuid;
}

/**
 * 记录后台日志公共函数
 * @param array $arrData
 * @param int $intSource
 * @return mixed
 * @throws Exception
 */
function setSystemLog(array $arrData, int $intSource = 1)
{
        $arrOperationBefore = issetArrKey($arrData, 'operationBefore', '');
        $arrOperationAfter = issetArrKey($arrData, 'operationAfter', '');
        $arrWhereData = issetArrKey($arrData, 'whereData', '');
        $arrSetData = [
                'uniqueId' => uuid(),
                'source' => $intSource,
                'userId' => request()->userId,
                'ip' => ip(),
                'path' => route(2)->getPath(),
                'method' => request()->getMethod(),
                'behavior' => issetArrKey($arrData, 'behavior', '未知'),
                'operationBefore' => json_encode($arrOperationBefore, JSON_UNESCAPED_UNICODE),
                'operationAfter' => json_encode($arrOperationAfter, JSON_UNESCAPED_UNICODE),
                'whereData' => json_encode($arrWhereData, JSON_UNESCAPED_UNICODE),
                'createTime' => time()
        ];
        return \App\Model\SystemLog::insert($arrSetData);
}

/**
 * 字符串转义防sql注入
 * @param $val
 * @return array|string|string[]|null
 */
function strAddslashes($val)
{
        if (is_array($val)) {
                foreach ($val as $k => $v) {
                        $val[$k] = strAddslashes($v);
                }
        } else {
                $val = preg_replace('/(?!<[^>]*)"(?![^<]*>)/', '&quot;', $val);
                if (!get_magic_quotes_gpc()) $val = addslashes($val);
        }
        return $val;
}

/**
 * 给数组添加前缀
 * @param array $arrData
 * @param string $strPrefix
 * @return array
 */
function arrKeyAddPrefix(array $arrData, string $strPrefix)
{
        $arrReturn = [];
        foreach ($arrData as $k => $v) {
                $arrReturn[$strPrefix . $k] = $v;
        }
        return $arrReturn;
}

/**
 * 判断是否为合法的身份证号码
 * @param string $vStr
 * @return bool
 */
function isIdCardNo(string $vStr)
{
        $vCity = array(
                '11', '12', '13', '14', '15', '21', '22',
                '23', '31', '32', '33', '34', '35', '36',
                '37', '41', '42', '43', '44', '45', '46',
                '50', '51', '52', '53', '54', '61', '62',
                '63', '64', '65', '71', '81', '82', '91'
        );
        if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $vStr)) {
                return false;
        }
        if (!in_array(substr($vStr, 0, 2), $vCity)) {
                return false;
        }
        $vStr = preg_replace('/[xX]$/i', 'a', $vStr);
        $vLength = strlen($vStr);
        if ($vLength == 18) {
                $vBirthday = substr($vStr, 6, 4) . '-' . substr($vStr, 10, 2) . '-' . substr($vStr, 12, 2);
        } else {
                return false;
                //$vBirthday = '19' . substr($vStr, 6, 2) . '-' . substr($vStr, 8, 2) . '-' . substr($vStr, 10, 2);
        }
        $vBirthdayTimestamp = strtotime($vBirthday);
        if (!$vBirthdayTimestamp || date('Y-m-d', $vBirthdayTimestamp) != $vBirthday) {
                return false;
        }
        if ($vLength == 18) {
                $vSum = 0;
                for ($i = 17; $i >= 0; $i--) {
                        $vSubStr = substr($vStr, 17 - $i, 1);
                        $vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
                }
                if ($vSum % 11 != 1) {
                        return false;
                }
        } else {
                return false;
        }
        return true;
}

/**
 * 发送http请求
 * @param array $arrRequestParam
 * @return array
 * @throws Exception
 */
function sendHttpRequest(array $arrRequestParam): array
{
        if (!isset($arrRequestParam['method'])) throw new \Exception('The request mode is not defined');
        if (!isset($arrRequestParam['url'])) throw new \Exception('The request address is not defined');
        $objHttpClient = new \Swoft\Swlib\HttpClient();
        $strUrl = $arrRequestParam['url'];
        $arrOptions = issetArrKey($arrRequestParam, 'options', []);
        switch ($arrRequestParam['method']) {
                case 'GET':
                        $objInitiate = $objHttpClient->get($strUrl, $arrOptions);
                        break;
                case 'POST':
                        $arrData = issetArrKey($arrRequestParam, 'data', []);
                        $objInitiate = $objHttpClient->post($strUrl, $arrData, $arrOptions);
                        break;
                case 'JSON':
                        $arrData = issetArrKey($arrRequestParam, 'data', []);
                        $objInitiate = $objHttpClient->json($strUrl, $arrData, $arrOptions);
                        break;
                default :
                        throw new \Exception('Unknown request mode');
        }
        return [
                'statusCode' => $objInitiate->getStatusCode(),
                'data' => $objInitiate->getBody()->getContents()
        ];
}

/**
 * 对数组进行分组聚合
 * @param array $array
 * @param $keys
 * @return array
 */
function arrayGroupBy(array $array, $keys): array
{
        if (!$array) return [];
        if (!is_array($keys) || count($keys) == 1) {
                $key = is_array($keys) ? array_shift($keys) : $keys;
                return array_reduce($array, function ($tmp_result, $item) use ($key) {
                        $tmp_result[$item[$key]][] = $item;
                        return $tmp_result;
                });
        } else {
                $keys = array_values($keys);
                $result = arrayGroupBy($array, array_shift($keys));
                foreach ($result as $k => $value) {
                        $result[$k] = arrayGroupBy($value, $keys);
                }
                return $result;
        }
}

/*
 * @description    取得两个时间戳相差的年龄
 * @before         较小的时间戳
 * @after          较大的时间戳
 * @return str     返回相差年龄y岁m月d天
**/
function dateDiffAge(int $intBefore, int $intAfter): array
{
        if ($intBefore > $intAfter) {
                $b = getdate($intAfter);
                $a = getdate($intBefore);
        } else {
                $b = getdate($intBefore);
                $a = getdate($intAfter);
        }
        $n = array(1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31);
        $y = $m = $d = 0;
        if ($a['mday'] >= $b['mday']) { //天相减为正
                if ($a['mon'] >= $b['mon']) {//月相减为正
                        $y = $a['year'] - $b['year'];
                        $m = $a['mon'] - $b['mon'];
                } else { //月相减为负，借年
                        $y = $a['year'] - $b['year'] - 1;
                        $m = $a['mon'] - $b['mon'] + 12;
                }
                $d = $a['mday'] - $b['mday'];
        } else {  //天相减为负，借月
                if ($a['mon'] == 1) { //1月，借年
                        $y = $a['year'] - $b['year'] - 1;
                        $m = $a['mon'] - $b['mon'] + 12;
                        $d = $a['mday'] - $b['mday'] + $n[12];
                } else {
                        if ($a['mon'] == 3) { //3月，判断闰年取得2月天数
                                $d = $a['mday'] - $b['mday'] + ($a['year'] % 4 == 0 ? 29 : 28);
                        } else {
                                $d = $a['mday'] - $b['mday'] + $n[$a['mon'] - 1];
                        }
                        if ($a['mon'] >= $b['mon'] + 1) { //借月后，月相减为正
                                $y = $a['year'] - $b['year'];
                                $m = $a['mon'] - $b['mon'] - 1;
                        } else { //借月后，月相减为负，借年
                                $y = $a['year'] - $b['year'] - 1;
                                $m = $a['mon'] - $b['mon'] + 12 - 1;
                        }
                }
        }
        return [$y, $m, $d];
        //return ($y == 0 ? '' : $y . '岁') . ($m == 0 ? '' : $m . '个月') . ($d == 0 ? '' : $d . '天');
}

/**
 * 名商通发送短信
 * @param $strPhone
 * @param $strMsg
 * @param string $strDate
 * @return false|string
 */
function mingShangTongSendMsg($strPhone, $strMsg, $strDate = "")
{
        $strLoginName = "鸿创网络";
        $strLoginPwd = "520@liyu";
        $strUrl = "http://203.81.21.34/send/gsend_utf8.asp?name={$strLoginName}&pwd={$strLoginPwd}&dst={$strPhone}&msg=" . urlencode($strMsg) . "&time={$strDate}";
        $fp = fopen($strUrl, "r");
        $ret = fgetss($fp, 255);
        fclose($fp);
        return $ret;
}

/**
 * 获取某年第几周的开始日期和结束日期
 * @param int $intYear
 * @param int $intWeek
 * @return array
 */
function weekAllDay(int $intYear, int $intWeek = 1): array
{
        $intYearStart = mktime(0, 0, 0, 1, 1, $intYear);
        $intYearEnd = mktime(0, 0, 0, 12, 31, $intYear);
        // 判断第一天是否为第一周的开始
        if (intval(date('W', $intYearStart)) === 1) {
                $intStart = $intYearStart;//把第一天做为第一周的开始
        } else {
                $intWeek++;
                $intStart = strtotime('+1 monday', $intYearStart);//把第一个周一作为开始
        }
        $arrWeekday = [];
        // 第几周的开始时间
        if ($intWeek === 1) {
                $arrWeekday['startTimestamp'] = $intStart;
        } else {
                $arrWeekday['startTimestamp'] = strtotime('+' . ($intWeek - 0) . ' monday', $intStart);
        }
        // 第几周的结束时间
        $arrWeekday['endTimestamp'] = strtotime('+1 sunday', $arrWeekday['startTimestamp']);
        if (date('Y', $arrWeekday['end']) != $intWeek) {
                $arrWeekday['endTimestamp'] = $intYearEnd;
        }
        return $arrWeekday;
}

/**
 * 获取一周相对应的日期
 * @param int|null $intTime
 * @param string $strFormat
 * @return array
 */
function weekOfDay(int $intTime = null, $strFormat = "Y-m-d")
{
        if ($intTime === null) $intTime = time();
        $intWeek = date('w', $intTime);
        $arrWeekName = array('星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日');
        //星期日排到末位
        if (empty($intWeek)) {
                $intWeek = 7;
        }
        $arrData = [];
        for ($i = 0; $i <= 6; $i++) {
                $arrData[$i]['date'] = date($strFormat, strtotime('+' . ($i + 1) - $intWeek . ' days', $intTime));
                $arrData[$i]['week'] = $arrWeekName[$i];
        }
        return $arrData;
}

/**
 * 获取一周开始和结束时间
 * @param int|null $intTime
 * @param int $intFirst 表示每周星期一为开始日期 0表示每周日为开始日期
 * @return array
 */
function weekStartEndDay(int $intTime = null, int $intFirst = 1): array
{
        //当前日期
        if ($intTime === null) $intTime = time();
        $strDate = date("Y-m-d", $intTime);
        //获取当前周的第几天 周日是 0 周一到周六是 1 - 6
        $intNowWeek = date('w', strtotime($strDate));
        //获取本周开始日期，如果$w是0，则表示周日，减去 6 天
        $intStartTimestamp = strtotime($strDate . ' -' . ($intNowWeek ? $intNowWeek - $intFirst : 6) . ' days');
        $strWeekStartDate = date('Y-m-d', $intStartTimestamp);
        //本周结束日期
        $intEndTimestamp = strtotime($strWeekStartDate . ' + 6 days');
        $strWeekEndDate = date('Y-m-d', $intEndTimestamp);
        return [
                'startTimestamp' => $intStartTimestamp,
                'endTimestamp' => $intEndTimestamp,
                'startDateTime' => $strWeekStartDate,
                'endDateTime' => $strWeekEndDate
        ];
}

/**
 * 通过生日计算年龄
 * @param int $intBirthday
 * @param bool $boolChild 是否小孩半岁计算法
 * @return float
 */
function birthdayToAge(int $intBirthday, bool $boolChild = true): float
{
        $arrDateDiffAge = dateDiffAge(time(), $intBirthday);
        $floatAge = $arrDateDiffAge[0];
        if ($boolChild && $floatAge < 6 && $arrDateDiffAge[1] >= 6) {
                $floatAge += 0.5;
        }
        return $floatAge;
}